#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Love2D 导出工具（Tkinter GUI）
功能：
- 生成 .love 压缩包（必备）
- 可选：使用指定的 love.exe 生成 Windows 独立 exe（把 love.exe + .love 拼接）
- 为 Android 生成基本工程模板（把 game.love 放到 app/src/main/assets/game.love），并生成 README 指南
- 提供 APK/JDK 的帮助说明弹窗
"""

import os
import sys
import zipfile
import shutil
import threading
import subprocess
from pathlib import Path
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from tkinter.scrolledtext import ScrolledText
import time

# -----------------------
# Helper functions
# -----------------------

IGNORED_DIRS = {'.git', '__pycache__', '.vs', '.idea', 'build', 'dist', '.vscode'}

def log_append(text_widget: ScrolledText, msg: str):
    text_widget.configure(state='normal')
    text_widget.insert(tk.END, f"[{time.strftime('%H:%M:%S')}] {msg}\n")
    text_widget.see(tk.END)
    text_widget.configure(state='disabled')

def create_love_archive(project_path: Path, output_path: Path, logger=None) -> Path:
    """
    Create a .love zip archive of project_path, saved as output_path.
    Returns the path to the created archive.
    """
    if logger: logger(f"开始创建 .love：{output_path}")
    # ensure parent exists
    output_path.parent.mkdir(parents=True, exist_ok=True)
    with zipfile.ZipFile(output_path, 'w', compression=zipfile.ZIP_DEFLATED) as zf:
        for root, dirs, files in os.walk(project_path):
            # filter dirs
            dirs[:] = [d for d in dirs if d not in IGNORED_DIRS]
            for f in files:
                # ignore some unwanted files
                if f.endswith(('.pyc', '.pyo', '.DS_Store')):
                    continue
                file_path = Path(root) / f
                # store relative path inside zip
                arcname = file_path.relative_to(project_path)
                zf.write(file_path, arcname.as_posix())
    if logger: logger(f".love 创建完成：{output_path}")
    return output_path

def make_windows_exe(love_exe_path: Path, love_archive_path: Path, out_exe_path: Path, logger=None) -> Path:
    """
    Create a Windows exe by copying love_exe and appending .love bytes.
    Also tries to copy nearby DLLs from love_exe parent directory if exist.
    """
    if logger: logger(f"开始创建 Windows 可执行文件：{out_exe_path}")
    out_dir = out_exe_path.parent
    out_dir.mkdir(parents=True, exist_ok=True)

    # copy all files from love_exe's directory that look necessary (love.exe, love.dll, etc)
    love_dir = love_exe_path.parent
    # base exe copy
    shutil.copy2(love_exe_path, out_exe_path)

    # append .love
    with open(out_exe_path, 'ab') as out_f, open(love_archive_path, 'rb') as in_f:
        out_f.write(in_f.read())

    if logger: logger(f"已生成 exe（拼接 .love）：{out_exe_path}")

    # try copy common runtime DLLs (optional)
    for candidate in ('love.dll', 'SDL2.dll', 'openal32.dll', 'lua51.dll', 'mpg123.dll'):
        src = love_dir / candidate
        if src.exists():
            try:
                shutil.copy2(src, out_dir / src.name)
                if logger: logger(f"复制依赖：{src.name}")
            except Exception as e:
                if logger: logger(f"复制依赖失败：{src.name} - {e}")

    return out_exe_path

def prepare_android_template(project_path: Path, love_archive_path: Path, out_dir: Path, project_name: str, logger=None) -> Path:
    """
    Create a simple android project template folder with the .love placed under:
    <out_dir>/android_build_<project_name>/app/src/main/assets/game.love
    Also generate a README.md with instructions (JDK/SDK/gradle/love-android).
    """
    if logger: logger("准备 Android 工程模板...")
    target = out_dir / f"android_build_{project_name}"
    assets_dir = target / "app" / "src" / "main" / "assets"
    assets_dir.mkdir(parents=True, exist_ok=True)

    # copy .love into assets
    dst_love = assets_dir / "game.love"
    shutil.copy2(love_archive_path, dst_love)
    if logger: logger(f"已把 .love 放到模板：{dst_love}")

    # create a small README with instructions
    readme = target / "README_BUILD_APK.md"
    readme.write_text(
        f"# Android 构建说明（为 {project_name}）\n\n"
        "此目录为构建准备：\n\n"
        "1. 需要准备的工具：\n"
        "   - Java JDK（建议 11 或 17）\n"
        "   - Android SDK（包含平台工具 platform-tools）\n"
        "   - Android NDK（如果使用原生模板）\n"
        "   - love-android or a Love2D Android gradle project template (推荐参考 https://github.com/love2d/love-android)\n\n"
        "2. 推荐流程（假设你已经拿到 love-android 模板）：\n"
        "   - 将本目录的 app/src/main/assets/game.love 覆盖到 love-android 的对应位置\n"
        "   - 在 love-android 目录中，使用 gradle / Android Studio 构建：\n"
        "       ./gradlew assembleRelease\n"
        "   - 构建成功后，APK 在 app/build/outputs/apk/ 下\n\n"
        "3. 如果你不知道如何安装 JDK：\n"
        "   - 在 Windows 上可以下载 OpenJDK 分发版（Adoptium/Temurin），安装后在环境变量中配置 JAVA_HOME\n"
        "   - 在命令行运行 `java -version` 验证\n\n"
        "4. 本工具只负责生成 game.love 并创建构建模板目录。\n"
    )
    if logger: logger(f"已生成 README：{readme}")
    return target

# -----------------------
# GUI
# -----------------------

class LoveExporterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Love2D 导出工具")
        self.root.minsize(720, 480)

        self.project_path = tk.StringVar()
        self.love_exe_path = tk.StringVar()
        self.jdk_path = tk.StringVar()
        self.export_windows = tk.BooleanVar(value=True)
        self.export_apk = tk.BooleanVar(value=False)
        self.output_dir = tk.StringVar(value=str(Path.cwd() / "love_exports"))

        self._build_ui()

    def _build_ui(self):
        pad = 8
        frm = ttk.Frame(self.root, padding=pad)
        frm.pack(fill=tk.BOTH, expand=True)

        # Project path
        row = ttk.Frame(frm)
        row.pack(fill=tk.X, pady=4)
        ttk.Label(row, text="项目文件夹路径（必需）:").pack(side=tk.LEFT)
        entry = ttk.Entry(row, textvariable=self.project_path)
        entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=6)
        ttk.Button(row, text="浏览", command=self.browse_project).pack(side=tk.LEFT)

        # love.exe path (optional)
        row = ttk.Frame(frm)
        row.pack(fill=tk.X, pady=4)
        ttk.Label(row, text="love.exe（可选，若在项目目录会自动检测）:").pack(side=tk.LEFT)
        e2 = ttk.Entry(row, textvariable=self.love_exe_path)
        e2.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=6)
        ttk.Button(row, text="浏览", command=self.browse_love_exe).pack(side=tk.LEFT)
        ttk.Button(row, text="自动检测", command=self.auto_detect_love).pack(side=tk.LEFT, padx=4)

        # Output dir
        row = ttk.Frame(frm)
        row.pack(fill=tk.X, pady=4)
        ttk.Label(row, text="输出目录:").pack(side=tk.LEFT)
        ttk.Entry(row, textvariable=self.output_dir).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=6)
        ttk.Button(row, text="浏览", command=self.browse_output).pack(side=tk.LEFT)

        # Platforms
        row = ttk.Frame(frm)
        row.pack(fill=tk.X, pady=6)
        ttk.Label(row, text="选择导出平台:").pack(side=tk.LEFT)
        ttk.Checkbutton(row, text="Windows (.exe)", variable=self.export_windows).pack(side=tk.LEFT, padx=6)
        cb_apk = ttk.Checkbutton(row, text="Android APK (生成模板)", variable=self.export_apk)
        cb_apk.pack(side=tk.LEFT, padx=6)
        # JDK help button near APK
        btn_jdk_help = ttk.Button(row, text="JDK & Android 构建说明", command=self.show_jdk_help)
        btn_jdk_help.pack(side=tk.LEFT, padx=6)

        # JDK path (optional)
        row = ttk.Frame(frm)
        row.pack(fill=tk.X, pady=4)
        ttk.Label(row, text="JDK 安装路径（可选，用于快速检测）：").pack(side=tk.LEFT)
        ttk.Entry(row, textvariable=self.jdk_path).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=6)
        ttk.Button(row, text="检查 java -version", command=self.check_java_version).pack(side=tk.LEFT)

        # Buttons
        row = ttk.Frame(frm)
        row.pack(fill=tk.X, pady=8)
        btn_export = ttk.Button(row, text="开始导出", command=self.on_export)
        btn_export.pack(side=tk.LEFT, padx=6)
        ttk.Button(row, text="打开输出目录", command=self.open_output_dir).pack(side=tk.LEFT, padx=6)
        ttk.Button(row, text="退出", command=self.root.quit).pack(side=tk.RIGHT, padx=6)

        # Log area
        lbl = ttk.Label(frm, text="日志")
        lbl.pack(anchor=tk.W, padx=2)
        self.log_text = ScrolledText(frm, height=12, state='disabled')
        self.log_text.pack(fill=tk.BOTH, expand=True, padx=4, pady=4)

    # -----------------
    # UI callbacks
    # -----------------
    def browse_project(self):
        path = filedialog.askdirectory(title="选择 Love2D 项目文件夹")
        if path:
            self.project_path.set(path)
            # auto-detect love.exe in selected folder
            self.auto_detect_love()

    def browse_love_exe(self):
        path = filedialog.askopenfilename(title="选择 love.exe",
                                          filetypes=[("EXE", "*.exe"), ("All files", "*.*")])
        if path:
            self.love_exe_path.set(path)

    def auto_detect_love(self):
        p = Path(self.project_path.get() or "")
        if not p.exists():
            messagebox.showinfo("自动检测", "请先选择项目文件夹（它是自动检测 love.exe 的地方）")
            return
        candidate = p / "love.exe"
        if candidate.exists():
            self.love_exe_path.set(str(candidate))
            self._log(f"在项目目录中发现 love.exe：{candidate}")
        else:
            # also look in parent directory
            candidate2 = p.parent / "love.exe"
            if candidate2.exists():
                self.love_exe_path.set(str(candidate2))
                self._log(f"在父目录发现 love.exe：{candidate2}")
            else:
                self._log("未在项目目录或其父目录找到 love.exe（如果有请手动指定）")

    def browse_output(self):
        path = filedialog.askdirectory(title="选择输出目录", initialdir=self.output_dir.get())
        if path:
            self.output_dir.set(path)

    def show_jdk_help(self):
        # Popup with explanation and basic tutorial
        text = (
            "JDK & Android 构建简要说明：\n\n"
            "为什么需要 JDK？\n"
            " - Android 的构建工具（Gradle / Android Studio）需要 Java（JDK）来运行。\n\n"
            "基本步骤（高层次）：\n"
            "1. 安装 JDK（建议 AdoptOpenJDK / Temurin，版本 11 或 17）并配置 JAVA_HOME。安装后在命令行运行：\n"
            "   java -version\n"
            "   javac -version\n\n"
            "2. 安装 Android SDK（建议使用 Android Studio），并确保 adb / sdkmanager 可用。\n\n"
            "3. 获取 love-android 模板（例如 https://github.com/love2d/love-android 或第三方模板），把本工具生成的 app/src/main/assets/game.love 放进去覆盖。\n\n"
            "4. 在 love-android 目录中运行：\n"
            "   ./gradlew assembleRelease\n"
            "   或 使用 Android Studio 打开并构建。生成的 APK 在 app/build/outputs/apk/ 下。\n\n"
            "本工具将为你生成 game.love 并创建一个 android_build_<project> 目录，里面含 README_BUILD_APK.md，参考上面的步骤进行最终打包。\n\n"
            "如果你需要，本工具可以尝试运行 `java -version`（需在本机已安装 Java），可在主界面填写 JDK 路径后点击“检查 java -version”。"
        )
        messagebox.showinfo("JDK & Android 构建说明", text)

    def check_java_version(self):
        jdk = self.jdk_path.get().strip()
        cmd = None
        if jdk:
            # try to use jdk bin/java
            java_bin = Path(jdk) / "bin" / ("java.exe" if os.name == 'nt' else "java")
            if java_bin.exists():
                cmd = [str(java_bin), "-version"]
            else:
                # maybe user provided JAVA_HOME-like path
                cmd = ["java", "-version"]
        else:
            cmd = ["java", "-version"]
        try:
            proc = subprocess.run(cmd, capture_output=True, text=True, timeout=8)
            output = proc.stderr.strip() or proc.stdout.strip()
            messagebox.showinfo("java -version", f"命令：{' '.join(cmd)}\n\n输出：\n{output}")
        except Exception as e:
            messagebox.showerror("错误", f"无法执行 java -version：{e}")

    def open_output_dir(self):
        out = Path(self.output_dir.get())
        out.mkdir(parents=True, exist_ok=True)
        try:
            if sys.platform == "win32":
                os.startfile(out)
            elif sys.platform == "darwin":
                subprocess.run(["open", str(out)])
            else:
                subprocess.run(["xdg-open", str(out)])
        except Exception as e:
            messagebox.showerror("打开失败", f"无法打开目录：{e}")

    def _log(self, msg: str):
        log_append(self.log_text, msg)

    def on_export(self):
        # validate inputs
        project = self.project_path.get().strip()
        if not project:
            messagebox.showwarning("缺少项目路径", "请指定 Love2D 项目文件夹路径（必需）。")
            return
        project_path = Path(project)
        if not project_path.exists() or not project_path.is_dir():
            messagebox.showerror("路径无效", "项目路径不存在或不是文件夹。")
            return
        if not (self.export_windows.get() or self.export_apk.get()):
            messagebox.showwarning("请选择平台", "请至少勾选一个导出平台（Windows 或 APK）。")
            return

        # start export in a thread to keep UI responsive
        t = threading.Thread(target=self._do_export, args=(project_path,), daemon=True)
        t.start()

    def _do_export(self, project_path: Path):
        try:
            self._log("==== 开始导出流程 ====")
            out_base = Path(self.output_dir.get())
            out_base.mkdir(parents=True, exist_ok=True)

            project_name = project_path.name
            love_name = f"{project_name}.love"
            love_archive_path = out_base / love_name

            # 1) create .love
            create_love_archive(project_path, love_archive_path, logger=self._log)

            # 2) Windows export
            if self.export_windows.get():
                love_exe = self.love_exe_path.get().strip()
                if love_exe:
                    love_exe_path = Path(love_exe)
                    if not love_exe_path.exists():
                        self._log(f"指定的 love.exe 不存在：{love_exe_path}。将只生成 .love 文件。")
                    else:
                        # create exe name
                        out_exe = out_base / f"{project_name}.exe"
                        try:
                            make_windows_exe(love_exe_path, love_archive_path, out_exe, logger=self._log)
                            self._log(f"Windows 可执行已生成：{out_exe}")
                        except Exception as e:
                            self._log(f"创建 Windows exe 失败：{e}")
                else:
                    # try autodetect inside project or parent
                    cand1 = project_path / "love.exe"
                    cand2 = project_path.parent / "love.exe"
                    found = None
                    if cand1.exists(): found = cand1
                    elif cand2.exists(): found = cand2
                    if found:
                        self._log(f"在本地自动检测到 love.exe：{found}，将作为基底生成 exe。")
                        try:
                            out_exe = out_base / f"{project_name}.exe"
                            make_windows_exe(found, love_archive_path, out_exe, logger=self._log)
                            self._log(f"Windows 可执行已生成：{out_exe}")
                        except Exception as e:
                            self._log(f"创建 Windows exe 失败：{e}")
                    else:
                        self._log("未指定 love.exe，且在项目/父目录未检测到 love.exe。已生成 .love 文件，但无法生成独立 exe。")

            # 3) APK template
            if self.export_apk.get():
                try:
                    android_dir = prepare_android_template(project_path, love_archive_path, out_base, project_name, logger=self._log)
                    self._log(f"已生成 Android 模板目录：{android_dir}")
                    self._log("请查看模板内的 README_BUILD_APK.md，按步骤使用 love-android 或 Android Studio 构建 APK。")
                except Exception as e:
                    self._log(f"生成 Android 模板失败：{e}")

            self._log("==== 导出完成 ====")
            messagebox.showinfo("完成", "导出流程已完成。请查看日志和输出目录。")
        except Exception as e:
            self._log(f"导出出现异常：{e}")
            messagebox.showerror("导出失败", f"发生错误：{e}")

# -----------------------
# Run app
# -----------------------

def main():
    root = tk.Tk()
    app = LoveExporterApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()
